<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h5,
        p {
            margin: 0;
        }
    </style>
</head>

<body>
    <button onclick="get_login()">创建空会话</button>
    <button onclick="post_login('/login')">登录 (补全会话)</button>
    <button onclick="fetch('/t2')">GET登录后限制内容</button>
    <button onclick="post_enc('/t2')">POST登录后限制内容</button>
    <br>
    <h5>ecdh_peer_public_key:</h5>
    <p id="ecdh_peer_public_key"></p>
    <h5>ecdh_shared_key:</h5>
    <p id="ecdh_shared_key"></p>
    <h5>ecdh_derive_key:</h5>
    <p id="ecdh_derive_key"></p>
    <script>
        var peer_public_key = null;
        var secret = ''
        async function get_login() {
            fetch('/login').then((res) => res.json()).then((res) => {
                peer_public_key = res['k']
                document.querySelector("#ecdh_peer_public_key").innerHTML = res['k']
            })
        }
        async function post_login() {
            const { keyPair, publicKeyBase64, privateKeyBase64 } = await generate_ECDH_KeyPair()

            const sharedKey = await compute_ECDH_SharedKey(keyPair, peer_public_key)
            const deriveKey = await compute_ECDH_DerivedKey(sharedKey)


            fetch('/login', {
                method: "post",
                body: JSON.stringify({ k: publicKeyBase64 }),
            }).then((res) => res.json()).then((res) => {
                document.querySelector("#ecdh_shared_key").innerHTML = sharedKey
                document.querySelector("#ecdh_derive_key").innerHTML = deriveKey
                if (res['test:derived_key'] == deriveKey) {
                    alert('密钥匹配成功!')
                } else {
                    alert('密钥匹配失败!')
                }
            })
        }
        function post(url) {
            fetch(url, {
                method: "post",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({ k: 'hello' }),
            })
        }
        function post_enc(url) {
            fetch(url, {
                method: "post",
                headers: {
                    "Content-Type": "application/enc-json"
                },
                body: "Y3k6Dp5yFpcAiNJzWJSC20CezEGVbOmc25pUN2EJvfRjVGxQAO0aOIwro13jAPsz92rAno4=",
            })
        }
    </script>
</body>

<script>


    async function generate_ECDH_KeyPair() {
        try {
            // 生成 ECDH 密钥对 (使用 P-256 曲线)
            const keyPair = await window.crypto.subtle.generateKey(
                { name: 'ECDH', namedCurve: 'P-256' },
                true,
                ['deriveKey', 'deriveBits']
            )
            // 导出公钥为 raw 格式
            const publicKeyRaw = await window.crypto.subtle.exportKey('raw', keyPair.publicKey)
            // 导出私钥为 pkcs8 格式
            const privateKeyPkcs8 = await window.crypto.subtle.exportKey(
                'pkcs8',
                keyPair.privateKey
            )
            // 转换为 Base64
            const publicKeyBase64 = arrayBuffer_to_base64(publicKeyRaw)
            const privateKeyBase64 = arrayBuffer_to_base64(privateKeyPkcs8)
            return {
                keyPair,
                publicKeyBase64,
                privateKeyBase64
            }
        } catch (error) {
            console.error('生成密钥对失败:', error)
            return {}
        }
    }

    async function compute_ECDH_SharedKey(
        keyPair,
        serverPublicKeyBase64
    ) {
        try {
            const serverPublicKeyRaw = base64_to_ArrayBuffer(serverPublicKeyBase64)
            const serverPublicKey = await window.crypto.subtle.importKey(
                'raw',
                serverPublicKeyRaw,
                { name: 'ECDH', namedCurve: 'P-256' },
                false,
                []
            )
            const sharedSecret = await window.crypto.subtle.deriveBits(
                { name: 'ECDH', public: serverPublicKey },
                keyPair.privateKey,
                256
            )
            const sharedSecretBase64 = arrayBuffer_to_base64(sharedSecret)
            return sharedSecretBase64
        } catch (error) {
            console.error('计算共享密钥失败:', error)
            alert('计算共享密钥失败: ' + error.message)
            return ''
        }
    }
    async function compute_ECDH_DerivedKey(
        sharedSecretBase64,
        salt = '1234',
        info = 'ECDH key derivation'
    ) {
        const saltBuffer = new TextEncoder().encode(salt)
        const infoBuffer = new TextEncoder().encode(info)

        const baseKey = await window.crypto.subtle.importKey(
            'raw',
            base64_to_ArrayBuffer(sharedSecretBase64),
            { name: 'HKDF' /*使用 HKDF 派生密钥（与Python端保持一致）*/ },
            false,
            ['deriveKey']
        )
        const derivedKey = await window.crypto.subtle.deriveKey(
            { name: 'HKDF', salt: saltBuffer, info: infoBuffer, hash: 'SHA-256' },
            baseKey,
            { name: 'AES-GCM', length: 256 },
            true,
            ['encrypt', 'decrypt']
        )
        const derivedKeyRaw = await window.crypto.subtle.exportKey('raw', derivedKey)
        const derivedKeyBase64 = arrayBuffer_to_base64(derivedKeyRaw)
        return derivedKeyBase64
    }




    function arrayBuffer_to_base64(buffer) {
        let binary = ''
        const bytes = new Uint8Array(buffer)
        for (let i = 0; i < bytes.byteLength; i++) {
            binary += String.fromCharCode(bytes[i])
        }
        return window.btoa(binary)
    }

    // 辅助函数: Base64 转 ArrayBuffer
    function base64_to_ArrayBuffer(base64) {
        const binaryString = window.atob(base64)
        const bytes = new Uint8Array(binaryString.length)
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i)
        }
        return bytes.buffer
    }
</script>

</html>